package main;

/*
 * Class offering the main functions of the library
 * @author M.T. Segarra
 * @version 0.0.1
 */

import java.util.*;

import loansManagement.Loan;
import loansManagement.LoanDAO;
import subscribersManagement.Subscriber;
import booksManagement.*;
import exceptions.*;
import subscribersManagement.SubscriberDAO;


/**
 * @author       user
 */
public class Library {

	/**
   * @uml.property  name="loans"
   * @uml.associationEnd  
   */
	private LoanDAO loans = new LoanDAO();

	/**
   * @uml.property  name="medias"
   * @uml.associationEnd  
   */
	private MediaDAO medias = new MediaDAO();
	
	/**
   * @uml.property  name="copies"
   * @uml.associationEnd  
   */
	private CopyDAO copies = new CopyDAO();

	/**
   * @uml.property  name="suscribers"
   * @uml.associationEnd  
   */
	private SubscriberDAO subscribers=new SubscriberDAO();


	/**
   * @return
   * @uml.property  name="loans"
   */
	public LoanDAO getLoans() {
		return this.loans;
	}
	
	/**
   * @param  loanDAO
   * @uml.property  name="loans"
   */
	public void setLoans(LoanDAO loanDAO){
		this.loans=loanDAO;
	}
	
	/**
   * @return
   * @uml.property  name="copies"
   */
	public CopyDAO getCopies(){
		return this.copies;
	}
	
	/**
   * @param  copies
   * @uml.property  name="copies"
   */
	public void setCopies(CopyDAO copies){
		this.copies = copies;
	}


	/**
   * @return
   * @uml.property  name="medias"
   */
	public MediaDAO getMedias() {
		return medias;
	}


	/**
   * @param  medias
   * @uml.property  name="medias"
   */
	public void setMedias(MediaDAO medias) {
		this.medias = medias;
	}


	private boolean isAMedia(Media media) throws BadParametersException, BookExistsException {
		if (media == null)
			throw new BadParametersException();
		
		// If the object is in the list,
		// it is a known book
		return medias.contains(media);
	}
	
	private boolean isACopy(Copy copy) throws BadParametersException, BookExistsException {
		if (copy == null)
			throw new BadParametersException();
		
		//If the object is in the list,
		//it is a known copy
		return copies.contains(copy);
	}


	/**
   * @return
   * @uml.property  name="suscribers"
   */
	public SubscriberDAO getSubscribers() {
		return subscribers;
	}


	/**
   * @param  subscribers
   * @uml.property  name="suscribers"
   */
	public void setSubscribers(SubscriberDAO subscribers) {
		this.subscribers = subscribers;
	}

	/**
	 * A utility method to know if a subscriber is known of the library
	 * 
	 * @param subscriber to
	 * @return false if the subscriber of the parameter is not known of the library
	 * @return true if the subscriber of the parameter is known of the library
	 */
	private boolean isASubscriber(Subscriber subscriber) throws BadParametersException, SubscriberExistsException {
		if (subscriber == null)
			throw new BadParametersException();
		
		if (subscriber.getNumber() == 0) return false;
		
		// If the object is in the list,
		// it is a known subscriber
		return this.subscribers.contains(subscriber);
	}

	/**
	 * Adds a new media to the library
	 * 
	 * @param media the media to be added
	 * @throws BadParametersException
	 * @throws BookExistsException
	 */
	public void addMedia(Media media) throws BadParametersException,
			BookExistsException {
		if (media == null)
			throw new BadParametersException();

		// Look if the media is already in the library
		if (isAMedia(media))
			throw new BookExistsException();

		this.medias.add(media);
		for (Copy c : media.getCopies()){
			this.copies.add(c);
			System.out.println(c.toString());
		}
	}
	
	public void addMediaCopy(Media media, int nbr) throws BadParametersException, BookExistsException{
		if (media==null)
			throw new BadParametersException();
		
		if (!isAMedia(media)){
			System.out.println("this media does not exist");
			throw new BookExistsException();
		}
			for (int i=0; i<nbr;i++){
				Copy c = media.addCopy();
				this.copies.add(c);
				System.out.println(c.toString());
			}		
		
	}
	
	/*public void addMediaCopies(Media media) throws BadParametersException, BookExistsException{
		if (media == null)
			throw new BadParametersException();
		for (Copy copy : media.getCopies()){
			if (isACopy(copy))
				throw new BookExistsException();
			else
				this.copies.add(copy);
		}
			
	}*/

	/**
	 * This method add data about a new subscriber
	 * 
	 * @param s the subscriber to be added
	 * @return subscriber number
	 * @throws BadParametersException
	 * @throws SubscriberExistsException
	 */
	public long addSubscriber(Subscriber s) throws BadParametersException,
			SubscriberExistsException {
		if (s == null)
			throw new BadParametersException();

		// Do not subscribe more than once a subscriber
		if (isASubscriber(s))
			throw new SubscriberExistsException();

		this.subscribers.add(s);
		System.out.println(s+ " added to the data base");
		return s.getNumber();
	}

	/**
	 * This method deletes all data of a book from the library
	 * 
	 * @param book the book to delete
	 * @throws BadParametersException
	 * @throws BookExistsException
	 * @throws LentBookException
	 */
	public void deleteMedia(Media media) throws BadParametersException, BookExistsException,
			LentBookException {
		if (media == null)
			throw new BadParametersException();

		// If the book does not exist, exception
		if (!isAMedia(media)){
			System.out.println("This is not a media");
			throw new BookExistsException();
		}

		// If at least one copy of the media is lent do not delete
		if (!media.noLentCopy()){
			System.out.println("Media copy lent. Cannot delete.");
			throw new LentBookException("Media copy lent. Cannot delete.");
			}
		for(Copy c : media.getCopies()){
			this.copies.remove(c);
		}
		this.medias.remove(media);
	}
	
	public void deleteCopy(Copy copy)throws BadParametersException, BookExistsException, LentBookException {
		if (copy == null)
			throw new BadParametersException();
		
		//If the copy does not exist, exception
		if (!isACopy(copy))
			throw new BookExistsException();
		
		//If the copy is lent do not delete
		if (copy.isLent())
			throw new LentBookException("Copy lent. Cannot delete");
		
		copy.getMedia().getCopies().remove(copy);
		this.copies.remove(copy);
		System.out.println("Copy deleted");
		
	}

	
	/**
	 * This method deletes data of a subscriber from the library
	 * @param subsNumber number of the subscriber
	 * @throws BadParametersException
	 * @throws SubscriberExistsException
	 */
	public void deleteSubscriber(Subscriber subscriber) throws BadParametersException,
			SubscriberWithLoansException, SubscriberExistsException {

		
		// If the number is not a subscriber number
		// raise exception
		if (!isASubscriber(subscriber))
			throw new SubscriberExistsException();

		// If loans, raise exception
		if (subscriber.existingLoans())
			throw new SubscriberWithLoansException();

		// Remove the subscriber
		this.subscribers.remove(subscriber);
	}

	/**
	 * Method to lend a book for a subscriber
	 * 
	 * @param book the book to be lent
	 * @param lender subscriber that whant to lend
	 * @return return date for the lent book
	 * @throws BadParametersException
	 * @throws BookExistsException
	 * @throws LentBookException
	 * @throws SubscriberExistsException
	 * @throws SubscriberWithLoansException
	 * @throws TooManyLoansException
	 * @throws ReservedCopyException 
	 */
	public Calendar lend(Copy copy, Subscriber lender) throws BadParametersException,
			BookExistsException, LentBookException, SubscriberExistsException, TooManyLoansException, ReservedCopyException {

		if (copy == null || lender == null)
			throw new BadParametersException();

		// Check if the media exists
		if (!isAMedia(copy.getMedia()))
			throw new BookExistsException();
		
		//Check if the copy exists
		if (!isACopy(copy))
			throw new BookExistsException();

		// Check if the subscriber exists
		if (!isASubscriber(lender))
			throw new SubscriberExistsException();

		// Make the loan if possible
		Loan l = new Loan(copy, lender);
		this.loans.add(l);
		return l.getReturnDate();
	}
	
	public void reserve(Media media, Subscriber reserver) throws BadParametersException, BookExistsException, SubscriberExistsException, TooManyReservationsException{
		if (media==null)
			throw new NullPointerException("The media is null");
		if (reserver==null)
			throw new NullPointerException("null value of parameter reserver");
		//Check if the media exists
		if (!isAMedia(media))
			throw new BookExistsException();
		// Check if the subscriber exists
		if (!isASubscriber(reserver))
			throw new SubscriberExistsException();
	//Make the reservation if possible and no available copy
		if (!media.allCopiesLent())
			System.out.println("Available copy of media " + media + " in library");
		else
			media.addReserver(reserver);
		
		
	}
	
	public void cancelReservation(Media media, Subscriber reserver) throws BookExistsException, SubscriberExistsException, BadParametersException{
		if (media==null)
			throw new NullPointerException("The media is null");
		if (reserver==null)
			throw new NullPointerException("null value of parameter reserver");
		//Check if the media exists
		if (!isAMedia(media))
			throw new BookExistsException();
		// Check if the subscriber exists
		if (!isASubscriber(reserver))
			throw new SubscriberExistsException();
		if (media.getReservers().remove(reserver))
			System.out.println("Reservation cancelled");
		else{
			System.out.println("this reservation does not exist");
		}
	}

	/**
	 * @return all medias of the library
	 */
	public List<Media> listMedias() {
		return this.medias.getContent();
	}

	/**
	 * Return the list of current loans
	 */
	public List<Loan> listLoans() {
		return this.loans.getContent();
	}

	/**
	 * @return all subscribers of the library
	 */
	public List<Subscriber> listSubscribers() {
		return this.subscribers.getContent();
	}

	/**
	 * This method returns the book book 
	 * @param book the book to be returned
	 * @throws BookExistsException
	 * @throws LentBookException
	 */
	public void returnCopy(Copy copy) throws BadParametersException, BookExistsException,
			LentBookException, LoanExistsException {
		if (copy == null)
			throw new BadParametersException();

		// Check if the book exists
		if (!isAMedia(copy.getMedia()))
			throw new BookExistsException();

		// Get the loan from the list
		//Loan l = copy.getLoan();
		//int index = loans.indexOf(copy.getLoan());
		
		// Ask the book to make return
		copy.returnCopy();

		// Delete the loan from my list
		//loans.remove(l);
	}
}
